React 生命周期
公式抽象
UI=render(data)
prop
prop为property的简写,意为属性
- prop是组件的对外接口,state是组件的内部状态,对外用prop,内部用state。
- 一个React组件通过定义自己能够接受的prop就定义了自己的对外公共接口。
- 每个React组件都是独自存在的模块,组件之外的一切都是外部世界,外部世界就是通过prop来和组件对话。
1 | <SampleButton id="sample" borderWidth={2} onClick={onButtonClick} style={{color:"red"}} /> |
- HTML组件的属性都是字符串类型,而React组件的prop能支持任何一种js语言数据类型,比如数字,函数,对象。
- 当prop的类型不是字符串类型的时候在JSX中必须用花括号{}把prop值包住。
父组件可以通过prop给子组件传递数据,同样子组件也可以通过prop给父组件反馈数据,因为prop不限制存数据,也可以是函数,函数类型的prop等于让父组件交给了子组件一个回调函数,子组件调用此prop的时候,可以带上必要的参数,这样就可以反馈数据给父组件。
1
2
3
4
5
6constructor(props) {
super(props);
this.state = {
count: props.initValue || 0
}
}propTypes 、defaultProps ( prop-types v16.0, babel-react-optimize 线上环境)
1
2
3
4
5
6
7
8
9
10
11
12
13// propTypes
import PropTypes from 'prop-types';
、
SampleButton.propTypes = {
id: PropTypes.string.isRequired,
borderWidth: PropTypes.number
}
SampleButton.defaultProps={
borderWidth: 0
}
例子
prop和state的对比
- prop用于定义外部接口,state用于记录内部状态;
- prop的赋值再外部世界实用组件时,state的赋值再组件内部;
- 组件不应该改变prop的值,而state存在的目的就是让组件来改变的;
组件的生命周期
装载过程
当组件第一次被渲染的时候,一次调用这些函数。
constructor
组件自己的构造函数,可用于初始化state,绑定this环境等
getInitialState & getDefaultProps
只有React.createClass创造的组件会有,用于初始化state和给props初始值
componentWillMount
在调用render函数之前调用,这里的所有事情都可以提前到constructor中做
render
一个React组件可以忽略其他所有函数都不实现,但是一定要实现render函数,因为所有React组件的父类React.Component类对其他生命周期函数都有默认实现。
- render 函数并不做实际的渲染动作,它只是返回一个JSX描述的结果,最终由React 来操作渲染过程。
componentDidMount
在调用render函数之后调用,注意点: - render函数调用完后,不会立刻调用componentDidMount,它的触发点在render已经引发了渲染,组件已经被装载到DOM数上后。
- 例子
- 不同于componentWillMount可以在服务器和浏览器端被调用,componentDidMount只能在浏览器端被调用。这点在同构的时候会用到,同时也提供给开发者一个很好的位置去做只有浏览器才做的逻辑,比如通过ALAX获取数据用来填充组件内容。
- 有时候React需要和其他UI库配合使用,比如jQuery,比如d3.js等,因为DOM已经存在,事件函数已设置好,所以可以在这一步进行调用。
更新过程
componentWillReceiveProps
- 只要是父组件的render函数被调用,不管传递的porps有没有改变,都会触发子组件的componentWillReceiveProps。
- 注意子组件内部的this.setState方法不会触发这个函数,因为这个函数是根据新的props(也就是参数nextProps)来计算是不是要更新state。
- 例子
shouldComponentUpdata(nextProps,nextState)
- 此函数决定了一个组件什么时候不需要渲染,shouldComponentUpdata返回一个布尔值,告诉React库这个组件在这次更新中是否要继续。
- 恰当的使用shouldComponentUpdata能够大大提高React组件的性能。
- 如果不写,会继承React.Component中的默认实现方式,也就是简单的返回true。
- 例子
扩展:React.PureComponent
React.PureComponent通过浅的prop和状态比较来实现shouldComponentUpdate() - 在将来,React会将shouldComponentUpdate()视为提示而不是严格的指令,并且返回false仍然可能导致组件的重新呈现。
componentWillUpdate
render
componentDidUpdate
- 如果组件的shouldComponentUpdata返回true,接下来就会依次调用这三个函数。
- 与装载不同的是,这一对函数的Did函数(componentDidUpdate)并不是只在浏览器端执行。
- 在React组件更新时,原有内容被重制,所以用到的UI库(比如jQuery)需要在componentDidUpdate后再次调用jQuery代码。
卸载过程
componentWillUnmount
当React组件需要从DOM树上删掉之前,对应的componentWillUnmount函数会被调用,移除多余的DOM元素,避免内存泄漏。
React v16.3新生命周期
开启异步渲染
新引入的两个生命周期函数 getDerivedStateFromProps,getSnapshotBeforeUpdate 以及在未来 v17.0 版本中即将被移除的三个生命周期函数 componentWillMount,componentWillReceiveProps,componentWillUpdate .
装载
- constructor()
- static getDerivedStateFromProps()
- render()
- componentDidMount()
- 移除 componentWillMount()
更新
- static getDerivedStateFromProps()
- shouldComponentUpdate()
- render()
- getSnapshotBeforeUpdate()
- componentDidUpdate()
- 移除 componentWillUpdate() componentWillReceiveProps()
getDerivedStateFromProps(nextProps, prevState)
getDerivedStateFromProps在调用render方法之前调用,无论是在初始安装还是后续更新。它应该返回一个更新状态的对象,或者返回null以不更新任何状态。
getSnapshotBeforeUpdate(prevProps, prevState)
getSnapshotBeforeUpdate()在最近呈现的输出被提交到例如DOM之前调用。它使您的组件可以在可能更改之前从DOM捕获一些信息(例如滚动位置)。此生命周期返回的任何值都将作为参数传递给componentDidUpdate()。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34可能出现在需要以特殊方式处理滚动位置的聊天线程等UI中。比如jtalk
class ScrollingList extends React.Component {
constructor(props) {
super(props);
this.listRef = React.createRef();
}
getSnapshotBeforeUpdate(prevProps, prevState) {
// 我们是否在列表中添加新项目?
// 捕获滚动位置,以便我们稍后调整滚动.
if (prevProps.list.length < this.props.list.length) {
const list = this.listRef.current;
return list.scrollHeight - list.scrollTop;
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
// 如果snapshot有返回值,我们就是添加了新项目.
// 调整滚动,以便这些新项目不会将旧项目推出视图.
// (这里的snapshot是从getSnapshotBeforeUpdate中返回的值)
if (snapshot !== null) {
const list = this.listRef.current;
list.scrollTop = list.scrollHeight - snapshot;
}
}
render() {
return (
<div ref={this.listRef}>{/* ...contents... */}</div>
);
}
}
在上面的例子中,重要的是读取scrollHeight属性,getSnapshotBeforeUpdate因为“渲染”阶段生命周期(如render)和“提交”阶段生命周期(如getSnapshotBeforeUpdate和componentDidUpdate)之间可能存在延迟。
错误处理
componentDidCatch(error, info)
错误边界是React组件,它们在其子组件树中的任何位置捕获JavaScript错误,记录这些错误,并显示回退UI而不是崩溃的组件树。错误边界在渲染期间,生命周期方法以及它们下面的整个树的构造函数中捕获错误。
1 | class ErrorBoundary extends React.Component { |